///////////////////////////////////////////////////////////////////////////////////////////////////
// Illusion Ray Development Kit (IDK)           Copyright (c) 2010 ( dimitriy.martynenko@gmail.com )
///////////////////////////////////////////////////////////////////////////////////////////////////
// Created : 2010-05-22
// Updated : ...
// Licence : NEW BSD
// Info	   : IDK Engine
///////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once

#include <Timer.hpp>

namespace Core
{	
	/*-------------------------------------------------------------------------
	| TResourceBase:
	---------------------------------------------------------------------------*/

	class TResourceAbstract : public TObject, virtual public IDK::IResource 
	{
		IDKClass;
	public:
		virtual bool OnTickLoad(void) = 0;
	};


	template<bool UseThreading>
	class TResourceBase;

	template<>
	class TResourceBase<Platform::Config::NoneThreadSafe> : public TResourceAbstract
	{
	public:
		virtual void OnLoad(float/* dt*/)
		{
			while(!OnTickLoad())
			{
			}

			SetLoadStatus(LoadStatus::Loaded);
		}
	};

	template<>
	class TResourceBase<Platform::Config::ThreadSafe> : public TResourceAbstract
	{
	private:
		Platform::Timer mResourceLoaderTimer;
	public:
		virtual void OnLoad(float dt)
		{
			mResourceLoaderTimer.Start();

			//TODO: need calc time and loop-call and estimate time
			while(dt > mResourceLoaderTimer.GetElapsed())
			{
				if(OnTickLoad())
				{
					SetLoadStatus(LoadStatus::Loaded);
					return;
				}
			}
		}
	};

	class TResource : public TResourceBase<Platform::Config::ThreadSafeMode>
	{
		IDKClass;
	protected:
		LoadStatus::Enum mLoadStatus;
		uint mLoadPriority;
		ref<IDK::IStream> mResourceStream;
		Threading::IThreadEvent& mSignal;
	public:
		TResource(Threading::IThreadEvent& signal, ref<IDK::IStream> stream, uint priority)
			: mSignal(signal),
			mResourceStream(stream),
			mLoadStatus(LoadStatus::Wait),
			mLoadPriority(priority)
		{
		}
	public:
		virtual void Cleanup(void)
		{
			mResourceStream->Close();
		}

		virtual ref<IDK::IStream> GetResourceStream(void)
		{
			return mResourceStream;
		}

		virtual LoadStatus::Enum GetLoadStatus(void)
		{
			return mLoadStatus;
		}

		virtual void OnChangeLoadStatus(LoadStatus::Enum code)
		{
			switch(code)
			{
			case LoadStatus::Cancel:
			case LoadStatus::Loading:
				mSignal.ReadyForProcessing();
				break;
			}
		}

		virtual void SetLoadStatus(LoadStatus::Enum status)
		{
			if(status != mLoadStatus)
			{
				OnChangeLoadStatus(status);
				mLoadStatus = status;
			}
		}

		virtual uint GetLoadPriority(void)
		{
			return mLoadPriority;
		}
	};

    class TResourceManager : public TArray<ref<IDK::IResource> >
	{
		typedef TArray<ref<IDK::IResource> > Base;
	private:
		bool mToProcess;
	public:
		TResourceManager() : Base(),
			mToProcess(false)
		{
		}

		void ToProcess()
		{
			mToProcess = true;
		}
		void Initialize(void);
		bool OnUpdate(float dt);
		ref<IDK::IResource> AddResource(ref<IDK::IResource> resource);
		ref<IDK::IStream> GetStream(const String& path);
	};

	typedef ref<TResourceManager> ResourceManager;

	//Abstract class
	class TStream : public TObject, virtual public IDK::IStream
	{
		IDKClass;
	protected:
		uint mPos;
		uint mSize;

		void CloseHandle(void)
		{
			mSize = Invalid;
		}

		inline String ReadStringBySize(uint size)
		{
			TDynamicArray<wchar> chars(size);

			ReadMemory(chars.GetMemory(), size * sizeof(wchar));
			return String(chars.GetMemory(), 0, size);
		}

	protected: //is abstract class
		TStream(void) : mSize(Invalid)
		{}
	public:
		virtual ~TStream(void)
		{
			CloseHandle();
		}

	public:
		virtual bool IsCanRead()
		{
			return mSize != mPos;
		}

		virtual void Close(void)
		{
			CloseHandle();
		}

		virtual uint GetSize(void)
		{
			return mSize;
		}

		virtual uint GetPos(void)
		{
			return mPos;
		}

		virtual void SetPos(uint pos)
		{
			mPos = pos;
		}

		virtual uint Seek(uint pos)
		{
			mPos += pos;
			return mPos;
		}

		virtual String ReadString(void)
		{
			return ReadStringBySize(Read<uint16>());
		}

		virtual String ReadText(void)
		{
			return ReadStringBySize(GetSize());
		}
	};

	class TFileStream : public TStream
	{
		typedef TStream Base;
		IDKClass; 
	private:
		Platform::FileHandle mHandle;
		String mPath;
	protected:
		void CloseHandle(void)
		{
			mHandle.Close();
			Base::CloseHandle();
		}
	public:
		TFileStream(const String &path): mPath(path)
		{}

		virtual ~TFileStream(void)
		{
			CloseHandle();
		}

		virtual bool IsOpen(void) const
		{
			return mHandle.IsValid();
		}

		virtual String GetPath(void) const
		{
			return mPath;
		}

		virtual void Open(bool create = false)
		{
			iLog(mPath->Chars());
			if(mHandle.Open(mPath->Chars(), create) == false)
			{
				iLog(mPath->Chars());
				if(create)
				{
					iError("Unable create file : ");
				}
				else
				{
					iError("Unable open file : ");
				}
			}
			
			mPos = 0;
		}

		virtual void Close(void)
		{
			CloseHandle();
		}

		virtual uint GetSize(void)
		{
			if(mSize == Invalid)
			{
				mSize = mHandle.GetSize();
			}

			return mSize;
		}

		virtual void SetPos(uint pos)
		{
			mHandle.SetPosition(mPos = pos);
		}

		virtual uint Seek(uint pos)
		{
			mPos += pos;
			mHandle.Seek(pos);
			return mPos;
		}

		virtual void ReadMemory(UnSafePtr ptr, uint size)
		{
			dword bread = 0;

			if(mHandle.Read(ptr, size, bread))
			{
				mPos += bread;
			}
		}
	};

	typedef ref<TFileStream> FileStream;

	class TFileDataStream : public TStream
	{
		typedef TStream Base;
	private:
		HandlePtr mHandle;
		FileStream mFile;
	protected:
		void CloseHandle(void)
		{
			mHandle = NullPtr;
			Base::CloseHandle();
		}
	public:
		TFileDataStream(FileStream file) : mFile(file) 
		{
		}

		virtual ~TFileDataStream(void)
		{
			CloseHandle();
		}

		virtual bool IsOpen(void) const
		{
			return mHandle != NullPtr;
		}

		virtual String GetPath(void) const
		{
			return mFile->GetPath();
		}

		virtual void Open(bool/* create = false*/)
		{
			mPos = 0;

			mFile->Open();
			
			mSize = mFile->GetSize();

			mHandle = new DynamicArray<wchar>::Create(mSize);

			mFile->ReadData(mHandle, mSize);

			mFile->Close();


		}

		virtual void Close()
		{
			CloseHandle();
		}

		virtual void ReadMemory(UnSafePtr ptr, uint size)
		{
			Platform::Memory::Copy<wchar>((wchar*)mHandle->GetPointer()+mPos, (wchar*)ptr, size);
			mPos += size;

			if(mPos > mSize)
			{
				mPos = mSize;
			}
		}
	};

	

	typedef ref<TFileDataStream> FileDataStream;
}
